"""
技术指标计算
"""
import numpy as np
import pandas as pd
from typing import Tuple, Optional


class Indicators:
    """技术指标计算类"""

    @staticmethod
    def calculate_rsi(prices: np.ndarray, period: int = 14) -> np.ndarray:
        """
        RSI (相对强弱指标)
        范围: 0-100
        > 70: 超买
        < 30: 超卖
        """
        deltas = np.diff(prices)

        # 创建与prices长度相同的gain和loss数组
        gains = np.zeros(len(prices))
        losses = np.zeros(len(prices))

        # 填充gain和loss数组
        for i in range(1, len(prices)):
            if deltas[i-1] > 0:
                gains[i] = deltas[i-1]
            elif deltas[i-1] < 0:
                losses[i] = -deltas[i-1]

        rsi = np.zeros_like(prices, dtype=float)
        rsi[:] = np.nan

        if len(prices) <= period:
            return rsi

        avg_gain = gains[1:period+1].mean()
        avg_loss = losses[1:period+1].mean()

        # 在period位置设置第一个RSI值
        if avg_loss == 0:
            rsi[period] = 100 if avg_gain > 0 else 50
        else:
            rs = avg_gain / avg_loss
            rsi[period] = 100 - (100 / (1 + rs))

        # EMA计算
        for i in range(period + 1, len(prices)):
            avg_gain = (avg_gain * (period - 1) + gains[i]) / period
            avg_loss = (avg_loss * (period - 1) + losses[i]) / period

            if avg_loss == 0:
                rsi[i] = 100 if avg_gain > 0 else 50
            else:
                rs = avg_gain / avg_loss
                rsi[i] = 100 - (100 / (1 + rs))

        return rsi

    @staticmethod
    def calculate_macd(prices: np.ndarray, fast: int = 12, slow: int = 26,
                      signal: int = 9) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
        """
        MACD (移动平均收敛散度)
        返回: (MACD线, 信号线, 直方图)

        交易信号:
        - MACD > 信号线: 买入信号
        - MACD < 信号线: 卖出信号
        - 直方图转正: 上升信号
        - 直方图转负: 下跌信号
        """
        ema_fast = Indicators.calculate_ema(prices, fast)
        ema_slow = Indicators.calculate_ema(prices, slow)

        macd_line = ema_fast - ema_slow
        signal_line = Indicators.calculate_ema(macd_line, signal)
        histogram = macd_line - signal_line

        return macd_line, signal_line, histogram

    @staticmethod
    def calculate_ema(prices: np.ndarray, period: int) -> np.ndarray:
        """EMA (指数移动平均)"""
        result = pd.Series(prices).ewm(span=period, adjust=False).mean()
        return result.values if isinstance(result, pd.Series) else result

    @staticmethod
    def calculate_sma(prices: np.ndarray, period: int) -> np.ndarray:
        """SMA (简单移动平均)"""
        return pd.Series(prices).rolling(window=period).mean().values

    @staticmethod
    def calculate_bollinger_bands(prices: np.ndarray, period: int = 20,
                                 std_dev: float = 2.0) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
        """
        布林带
        返回: (中线, 上线, 下线)

        交易信号:
        - 价格触及上线: 超买信号
        - 价格触及下线: 超卖信号
        """
        sma = Indicators.calculate_sma(prices, period)
        std = pd.Series(prices).rolling(window=period).std().values
        upper = sma + (std * std_dev)
        lower = sma - (std * std_dev)

        return sma, upper, lower

    @staticmethod
    def calculate_atr(high: np.ndarray, low: np.ndarray, close: np.ndarray,
                     period: int = 14) -> np.ndarray:
        """
        ATR (平均真实波幅)
        用于衡量波动率
        """
        tr1 = high - low
        tr2 = np.abs(high - np.roll(close, 1))
        tr3 = np.abs(low - np.roll(close, 1))

        tr = np.maximum(tr1, np.maximum(tr2, tr3))
        atr = pd.Series(tr).rolling(window=period).mean().values

        return atr

    @staticmethod
    def calculate_stoch(high: np.ndarray, low: np.ndarray, close: np.ndarray,
                       period: int = 14, smooth_k: int = 3, smooth_d: int = 3) -> Tuple[np.ndarray, np.ndarray]:
        """
        Stochastic Oscillator (随机振荡器)
        返回: (%K, %D)

        > 80: 超买
        < 20: 超卖
        """
        lowest_low = pd.Series(low).rolling(window=period).min().values
        highest_high = pd.Series(high).rolling(window=period).max().values

        k_percent = 100 * (close - lowest_low) / (highest_high - lowest_low + 1e-10)
        k_percent = pd.Series(k_percent).rolling(window=smooth_k).mean().values
        d_percent = pd.Series(k_percent).rolling(window=smooth_d).mean().values

        return k_percent, d_percent

    @staticmethod
    def calculate_roc(prices: np.ndarray, period: int = 12) -> np.ndarray:
        """
        ROC (变化率指标)
        计算价格相对于N周期前的变化百分比
        """
        roc = np.zeros_like(prices, dtype=float)
        roc[:] = np.nan

        for i in range(period, len(prices)):
            roc[i] = ((prices[i] - prices[i - period]) / prices[i - period]) * 100

        return roc

    @staticmethod
    def calculate_obv(close: np.ndarray, volume: np.ndarray) -> np.ndarray:
        """
        OBV (能量潮)
        用于判断买卖力度
        """
        obv = np.zeros_like(close, dtype=float)
        obv[0] = volume[0]

        for i in range(1, len(close)):
            if close[i] > close[i-1]:
                obv[i] = obv[i-1] + volume[i]
            elif close[i] < close[i-1]:
                obv[i] = obv[i-1] - volume[i]
            else:
                obv[i] = obv[i-1]

        return obv
